#include "main.h"

static const char *TAG = "i80_main";
static int countScreen;
lv_timer_t *updata_task1 = NULL;
lv_timer_t *updata_task2 = NULL;
/**
 * @brief 播放gif图片
 *
 */
void lv_example_gif_1(void)
{
    /**
     * Open a GIF image from a file and a variable
     */
    LV_IMG_DECLARE(fan);
    lv_obj_t *img;

    img = lv_gif_create(lv_scr_act());                      //
    lv_obj_set_size(img, LV_SIZE_CONTENT, LV_SIZE_CONTENT); /// 自适应大小
    // lv_img_set_zoom(img,450);//缩放图片
    lv_gif_set_src(img, &fan);
    lv_obj_align(img, LV_ALIGN_CENTER, 200, -119);
}


/**
 * @brief 解析AIDA64数据
 *
 * @param s 传入指针
 * @param data 解析数据类型
 */
static void func(char *s, uint8_t data)
{
    char *p, *token;
    switch (data)
    {
    case 1:
        p = strstr(s, "CPU fequence ") + strlen("CPU fequence "); // 待截取字符串前的字符串
        token = strtok(p, "M");
        myPc.cpuFrequence = atoi(token);
#if AIDA64_LOG
        printf("CPU fequence strtok %s\n", token);           // 打印提取出来的字符串
        printf("CPU fequence atoi %d\n", myPc.cpuFrequence); // 打印字符串转换成整型数据
#endif
        break;
    case 2:
        p = strstr(s, "CPU usage ") + strlen("CPU usage "); // 待截取字符串前的字符串
        token = strtok(p, "%");
        myPc.cpuUsage = atoi(token);
#if AIDA64_LOG
        printf("CPU usage strtok %s\n", token);       // 打印提取出来的字符串
        printf("CPU usage atoi %d\n", myPc.cpuUsage); // 打印字符串转换成整型数据
#endif
        break;
    case 3:
        p = strstr(s, "CPU temp ") + strlen("CPU temp "); // 待截取字符串前的字符串
        token = strtok(p, "C");
        myPc.cpuTemp = atoi(token);
#if AIDA64_LOG
        printf("CPU temp strtok %s\n", token);      // 打印提取出来的字符串
        printf("CPU temp atoi %d\n", myPc.cpuTemp); // 打印字符串转换成整型数据
#endif
        break;
    case 4:
        p = strstr(s, "RAM usage ") + strlen("RAM usage "); // 待截取字符串前的字符串
        token = strtok(p, "%");
        myPc.ramUsage = atoi(token);
#if AIDA64_LOG
        printf("RAM usage strtok %s\n", token);       // 打印提取出来的字符串
        printf("CPU usage atoi %d\n", myPc.ramUsage); // 打印字符串转换成整型数据
#endif
        break;
    case 5:
        p = strstr(s, "RAM available ") + strlen("RAM available "); // 待截取字符串前的字符串
        token = strtok(p, "M");
        myPc.ramAvaliable = atoi(token);
#if AIDA64_LOG
        printf("RAM available strtok %s\n", token);           // 打印提取出来的字符串
        printf("CPU available atoi %d\n", myPc.ramAvaliable); // 打印字符串转换成整型数据
#endif
        break;
    case 6:
        p = strstr(s, "SSD1 ") + strlen("SSD1 "); // 待截取字符串前的字符串
        token = strtok(p, "%");
        myPc.ssd1Used = atoi(token);
#if AIDA64_LOG
        printf("SSD1 Used strtok %s\n", token);       // 打印提取出来的字符串
        printf("SSD1 Used atoi %d\n", myPc.ssd1Used); // 打印字符串转换成整型数据
#endif
        break;
    case 7:
        p = strstr(s, "SSD2 ") + strlen("SSD2 "); // 待截取字符串前的字符串
        token = strtok(p, "%");
        myPc.ssd2Used = atoi(token);
#if AIDA64_LOG
        printf("SSD2 Used strtok %s\n", token);       // 打印提取出来的字符串
        printf("SSD2 Used atoi %d\n", myPc.ssd2Used); // 打印字符串转换成整型数据
#endif
        break;
    case 8:
        p = strstr(s, "NIC1 ") + strlen("NIC1 "); // 待截取字符串前的字符串
        token = strtok(p, "KB/s");
        myPc.NIC1DownSpeed = atof(token);
#if AIDA64_LOG
        printf("NIC1 strtok %s\n", token);              // 打印提取出来的字符串
        printf("NIC1 atof %.1f\n", myPc.NIC1DownSpeed); // 打印字符串转换成整型数据
#endif
        break;
    default:
        break;
    }

    //     p = strstr(s, "RAM available ") + strlen("RAM available "); //待截取字符串前的字符串
    //     q = strstr(p, "MB");                                        //待截取字符串后的字符
    //     printf("RAM available %.*s\n", (int)(q - p), p);            //打印提取出来的字符串
}

/**
 * @brief http_get_task
 *
 * @param arg
 */
static void http_get_task(void *arg)
{
    const struct addrinfo hints = {
        .ai_family = AF_INET,
        .ai_socktype = SOCK_STREAM,
    };
    struct addrinfo *res;
    struct in_addr *addr;
    int s, r;
    char recv_buf[512]; // 数据接收缓存区（一定要比接收的数据多，否则崩盘）
    // char mid_buf[512];  //数据提取处理缓存区

    while (1)
    { // DNS域名解析
        int err = getaddrinfo(WEB_SERVER, WEB_PORT, &hints, &res);

        if (err != 0 || res == NULL)
        {
            ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p", err, res);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            continue;
        }

        /* Code to print the resolved IP.

           Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */
        // 打印获取的IP
        addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
        ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*addr));

        // 新建socket
        s = socket(res->ai_family, res->ai_socktype, 0);
        if (s < 0)
        {
            ESP_LOGE(TAG, "... Failed to allocate socket.");
            freeaddrinfo(res);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            continue;
        }
        ESP_LOGI(TAG, "... allocated socket");

        // 连接ip
        if (connect(s, res->ai_addr, res->ai_addrlen) != 0)
        {
            ESP_LOGE(TAG, "... socket connect failed errno=%d", errno);
            close(s);
            freeaddrinfo(res);
            vTaskDelay(4000 / portTICK_PERIOD_MS);
            continue;
        }
        ESP_LOGI(TAG, "... connected");
        freeaddrinfo(res);

        // 发送http包
        if (write(s, REQUEST, strlen(REQUEST)) < 0)
        {
            ESP_LOGE(TAG, "... socket send failed");
            close(s);
            vTaskDelay(4000 / portTICK_PERIOD_MS);
            continue;
        }
        ESP_LOGI(TAG, "... socket send success");

        struct timeval receiving_timeout;
        receiving_timeout.tv_sec = 5;
        receiving_timeout.tv_usec = 0;
        if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &receiving_timeout,
                       sizeof(receiving_timeout)) < 0)
        {
            ESP_LOGE(TAG, "... failed to set socket receiving timeout");
            close(s);
            vTaskDelay(4000 / portTICK_PERIOD_MS);
            continue;
        }
        ESP_LOGI(TAG, "... set socket receiving timeout success");

        // 清缓存
        //  memset(mid_buf, 0, sizeof(mid_buf));

        // 获取http应答包
        /* Read HTTP response */
        do
        {
            bzero(recv_buf, sizeof(recv_buf));
            r = read(s, recv_buf, sizeof(recv_buf) - 1);
            // for (int i = 0; i < r; i++)
            // {
            //     putchar(recv_buf[i]); //打印接收到的数据
            // }
            // strcat(mid_buf, recv_buf); //提取出缓存数据 用strcopy也可

            // 字符串解析||json解析----->（ADIA64的数据为非json，故此处简单截取字符串指定字符后的数据即可）
            funcCount--;
            if (funcCount == 0)
                funcCount = 8;
            func(recv_buf, funcCount);
            // func(recv_buf, cpu_usage);
            // func(recv_buf, cpu_temp);
            // func(recv_buf, ram_usage);
            // func(recv_buf, ram_avaliable);

        } while (r > 0);

        // 延时一会
        ESP_LOGI(TAG, "... done reading from socket. Last read return=%d errno=%d.", r, errno);
        close(s);
        for (int countdown = 10; countdown >= 0; countdown--)
        {
            ESP_LOGI(TAG, "%d... ", countdown);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
        ESP_LOGI(TAG, "Starting again!");
    }
}

/**
 * @brief gif_demo
 *
 * @param NULL
 */
static void gif_demo(void)
{
    LV_IMG_DECLARE(harmony);
    lv_obj_t *img;

    img = lv_gif_create(lv_scr_act());
    lv_gif_set_src(img, &harmony);
    lv_obj_align(img, LV_ALIGN_LEFT_MID, 20, 0);

    img = lv_gif_create(lv_scr_act());
    /* Assuming a File system is attached to letter 'A'
     * E.g. set LV_USE_FS_STDIO 'A' in lv_conf.h */
    lv_gif_set_src(img, "A:lvgl/examples/libs/gif/bulb.gif");
    lv_obj_align(img, LV_ALIGN_RIGHT_MID, -20, 0);
}

/**
 * @brief increase_lvgl_tick 心跳
 *
 * @param arg
 */
static void increase_lvgl_tick(void *arg)
{
    /* Tell LVGL how many milliseconds has elapsed */
    lv_tick_inc(LVGL_TICK_PERIOD_MS);
}

/**
 * @brief updata_task1_cb 数据更新回调任务
 *
 * @param tmr
 */
void updata_task1_cb(lv_timer_t *tmr)
{
    char disbuf[30]; /*lvgl显示数据缓存*/

    if (SNTP_LOG)
        ESP_LOGI(TAG, "Enter lv_timer1 cb");

    /*显示时间更新*/
    sprintf(disbuf, "%d", NTP.year);
    lv_label_set_text(ui_year, disbuf);
    /*月*/
    sprintf(disbuf, "%d", NTP.month);
    lv_label_set_text(ui_month, disbuf);
    /*日*/
    sprintf(disbuf, "%d", NTP.mday);
    lv_label_set_text(ui_Day, disbuf);
    /*时*/
    sprintf(disbuf, "%d", NTP.hour);
    lv_label_set_text(ui_hours, disbuf);
    /*分*/
    if (NTP.min < 10)
    {
        sprintf(disbuf, "0%d", NTP.min);
        lv_label_set_text(ui_min, disbuf);
    }
    else
    {
        sprintf(disbuf, "%d", NTP.min);
        lv_label_set_text(ui_min, disbuf);
    }
    /*秒*/
    if (NTP.sec < 10)
    {
        sprintf(disbuf, "0%d", NTP.sec);
        lv_label_set_text(ui_sec, disbuf);
    }
    else
    {
        sprintf(disbuf, "%d", NTP.sec);
        lv_label_set_text(ui_sec, disbuf);
    }
    /*星期*/
    switch (NTP.day)
    {
    case 1:
        sprintf(disbuf, "Monday");
        lv_label_set_text(ui_day, disbuf);
        break;
    case 2:
        sprintf(disbuf, "Tuesday");
        lv_label_set_text(ui_day, disbuf);
        break;
    case 3:
        sprintf(disbuf, "Wednesday");
        lv_label_set_text(ui_day, disbuf);
        break;
    case 4:
        sprintf(disbuf, "Thursday");
        lv_label_set_text(ui_day, disbuf);
        break;
    case 5:
        sprintf(disbuf, "Friday");
        lv_label_set_text(ui_day, disbuf);
        break;
    case 6:
        sprintf(disbuf, "Saturday");
        lv_label_set_text(ui_day, disbuf);
        break;
    case 0:
        sprintf(disbuf, "Sunday");
        lv_label_set_text(ui_day, disbuf);
        break;
    default:
        break;
    }

    /*显示电脑性能以及占用状态*/
    sprintf(disbuf, "%d MHz", myPc.cpuFrequence);
    lv_label_set_text(ui_CPUfequenceB, disbuf); // CPU fequence

    sprintf(disbuf, "%d %%", myPc.cpuUsage);
    lv_label_set_text(ui_CPUUse, disbuf); // CPU used

    sprintf(disbuf, "%d 'C", myPc.cpuTemp);
    lv_label_set_text(ui_CPUTempB, disbuf); // CPU temp

    sprintf(disbuf, "%d MB", myPc.ramAvaliable);
    lv_label_set_text(ui_RAMB, disbuf); // RAM Avaliable

    lv_bar_set_value(ui_RAM1, myPc.ramUsage, LV_ANIM_OFF); // RAM Use

    sprintf(disbuf, "%d\n%%", myPc.ssd1Used);
    lv_label_set_text(ui_SSD1B, disbuf);                    // SSD1 Used %
    lv_bar_set_value(ui_SSD1D, myPc.ssd1Used, LV_ANIM_OFF); // SSD1 Used bar

    sprintf(disbuf, "%d\n%%", myPc.ssd2Used);
    lv_label_set_text(ui_SSD2B, disbuf);                    // SSD2 Used %
    lv_bar_set_value(ui_SSD2D, myPc.ssd2Used, LV_ANIM_OFF); // SSD2 Used bar

    sprintf(disbuf, "%.1f\nKB/s", myPc.NIC1DownSpeed);
    lv_label_set_text(ui_NIC1DownSpeed, disbuf); // NIC1 Down Speed
}

/**
 * @brief lv_Clock_Animation_Time_cb
 *
 * @param tmr
 */
void lv_UpdateScreen_Time_cb(lv_timer_t *tmr)
{
    ESP_LOGI(TAG, "SystemResetTime--- %d", SystemReset);
    // vTaskDelay(pdMS_TO_TICKS(1000));
    countScreen++;
    if (countScreen == 20)
    {
        _ui_screen_change(ui_Screen2, LV_SCR_LOAD_ANIM_MOVE_TOP, 500, 0);
    }
    if (countScreen == 30)
    {
        countScreen = 0;
        SystemReset--;
        if (SystemReset == 0) // one'half hour later Reset
        {
            // printf("Restarting now.\n");
            // esp_restart(); // Reset
            SystemReset=0;
        }
        else
            _ui_screen_change(ui_Screen1, LV_SCR_LOAD_ANIM_MOVE_TOP, 500, 0);
    }
}

/**
 * @brief gui_task
 *
 * @param arg
 */
static void gui_task(void *arg)
{
    /*lvgl内核初始化*/
    lv_init();
    /*屏幕初始化*/
    lv_port_disp_init();
    /*设备初始化接入*/
    // lv_port_indev_init();

    ESP_LOGI(TAG, "Install LVGL tick timer");
    // Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
    const esp_timer_create_args_t lvgl_tick_timer_args = {
        .callback = &increase_lvgl_tick,
        .name = "lvgl_tick"};
    esp_timer_handle_t lvgl_tick_timer = NULL;
    ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, LVGL_TICK_PERIOD_MS * 1000));

    ESP_LOGI(TAG, "Start Run UI||Demo");
    ui_init();
    /*lvgl定时任务*/
    updata_task1 = lv_timer_create(updata_task1_cb, 200, 0);
    updata_task2 = lv_timer_create(lv_UpdateScreen_Time_cb, 1000, 0);
    // lv_demo_widgets();
    // lv_demo_music();
    // lv_demo_benchmark();
    // lv_demo_stress();
    // lv_demo_keypad_encoder();
    while (1)
    {
        // raise the task priority of LVGL and/or reduce the handler period can improve the performance
        vTaskDelay(pdMS_TO_TICKS(10));
        // The task running lv_timer_handler should have lower priority than that running `lv_tick_inc`
        lv_timer_handler();
    }
}

/**
 * @brief Get_NTP_task
 * @param arg
 */
static void Get_NTP_task(void *)
{
    // char disbuf[30];    /*lvgl显示数据缓存*/
    time_t now;         // 定义一个now 存放unix 时间戳
    struct tm timeinfo; // 定义tm时间结构体
    /*
            int tm_sec;         秒，范围从 0 到 59
            int tm_min;         分，范围从 0 到 59
            int tm_hour;        小时，范围从 0 到 23
            int tm_mday;       一月中的第几天，范围从 1 到 31
            int tm_mon;         月份，范围从 0 到 11
            int tm_year;        注意是自 1900 起的年数
            int tm_wday;      一周中的第几天，范围从 0 到 6
            int tm_yday;        一年中的第几天，范围从 0 到 365
            int tm_isdst;       夏令时
    */
    obtain_time();                // 获取时间
    time(&now);                   // 获取总秒数时间
    localtime_r(&now, &timeinfo); // 将now unix时间戳格式转为timeinfo时间结构体

    while (1)
    {
        // Is time set? If not, tm_year will be (1970 - 1900).
        if (timeinfo.tm_year < (2016 - 1900)) // 判断timeinfo是否正确否者重新获取
        {
            ESP_LOGI(TAG, "Time is not set yet. Connecting to WiFi and getting time over NTP.");
            obtain_time(); // 获取时间
            // update 'now' variable with current time
            time(&now); // 获取总秒数时间
        }
        time(&now);               // 获取unix时间戳
        setenv("TZ", "CST-8", 1); // 设置为东八区，中国是东八区
        tzset();
        localtime_r(&now, &timeinfo); // 转为tm结构体

#if SNTP_LOG
        /*打印时间*/
        ESP_LOGI(TAG, "实时时间：周%d,%d年:%d月:%d日,%d:%d:%d",
                 timeinfo.tm_wday, timeinfo.tm_year + 1900,
                 timeinfo.tm_mon + 1, timeinfo.tm_mday,
                 timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
#endif

        NTP.year = timeinfo.tm_year + 1900;
        NTP.month = timeinfo.tm_mon + 1;
        NTP.mday = timeinfo.tm_mday;
        NTP.hour = timeinfo.tm_hour;
        NTP.min = timeinfo.tm_min;
        NTP.sec = timeinfo.tm_sec;
        NTP.day = timeinfo.tm_wday;

        vTaskDelay(500 / portTICK_PERIOD_MS);
    }
}

/**
 * @brief getEsp32S3CoreTemperatureTask
 *
 * @param arg
 */
static void temperature_sensor(void *arg)
{
    ESP_LOGI(TAG, "Install temperature sensor, expected temp ranger range: 10~50 ℃");
    temperature_sensor_handle_t temp_sensor = NULL;
    temperature_sensor_config_t temp_sensor_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(10, 50);
    ESP_ERROR_CHECK(temperature_sensor_install(&temp_sensor_config, &temp_sensor));

    ESP_LOGI(TAG, "Enable temperature sensor");
    ESP_ERROR_CHECK(temperature_sensor_enable(temp_sensor));

    ESP_LOGI(TAG, "Read temperature");
    float tsens_value;
    while (1)
    {
        ESP_ERROR_CHECK(temperature_sensor_get_celsius(temp_sensor, &tsens_value));
        ESP_LOGI(TAG, "Temperature value %.02f ℃", tsens_value);
        vTaskDelay(pdMS_TO_TICKS(5000));
    }
}

/**
 * @brief sdcard_test
 *
 * @param arg
 */
static void sdcard_test(void)
{
    esp_err_t ret;

    // Options for mounting the filesystem.
    // If format_if_mount_failed is set to true, SD card will be partitioned and
    // formatted in case when mounting fails.
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
#ifdef CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED
        .format_if_mount_failed = true,
#else
        .format_if_mount_failed = false,
#endif // EXAMPLE_FORMAT_IF_MOUNT_FAILED
        .max_files = 5,
        .allocation_unit_size = 16 * 1024};
    sdmmc_card_t *card;
    const char mount_point[] = MOUNT_POINT;
    ESP_LOGI(TAG, "Initializing SD card");

    // Use settings defined above to initialize SD card and mount FAT filesystem.
    // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
    // Please check its source code and implement error recovery when developing
    // production applications.

    ESP_LOGI(TAG, "Using SDMMC peripheral");
    sdmmc_host_t host = SDMMC_HOST_DEFAULT();

    // This initializes the slot without card detect (CD) and write protect (WP) signals.
    // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
    sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();

    // Set bus width to use:
#ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
    slot_config.width = 4;
#else
    slot_config.width = 1;
#endif

    // On chips where the GPIOs used for SD card can be configured, set them in
    // the slot_config structure:
#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
    slot_config.clk = CONFIG_EXAMPLE_PIN_CLK;
    slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD;
    slot_config.d0 = CONFIG_EXAMPLE_PIN_D0;
#ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
    slot_config.d1 = CONFIG_EXAMPLE_PIN_D1;
    slot_config.d2 = CONFIG_EXAMPLE_PIN_D2;
    slot_config.d3 = CONFIG_EXAMPLE_PIN_D3;
#endif // CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
#endif // CONFIG_SOC_SDMMC_USE_GPIO_MATRIX

    // Enable internal pullups on enabled pins. The internal pullups
    // are insufficient however, please make sure 10k external pullups are
    // connected on the bus. This is for debug / example purpose only.
    slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;

    ESP_LOGI(TAG, "Mounting filesystem");
    ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);

    if (ret != ESP_OK)
    {
        if (ret == ESP_FAIL)
        {
            ESP_LOGE(TAG, "Failed to mount filesystem. "
                          "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
        }
        else
        {
            ESP_LOGE(TAG, "Failed to initialize the card (%s). "
                          "Make sure SD card lines have pull-up resistors in place.",
                     esp_err_to_name(ret));
        }
        return;
    }
    ESP_LOGI(TAG, "Filesystem mounted");

    // Card has been initialized, print its properties
    sdmmc_card_print_info(stdout, card);

    // Use POSIX and C standard library functions to work with files:

    // First create a file.
    const char *file_hello = MOUNT_POINT "/hello.txt";

    ESP_LOGI(TAG, "Opening file %s", file_hello);
    FILE *f = fopen(file_hello, "w");
    if (f == NULL)
    {
        ESP_LOGE(TAG, "Failed to open file for writing");
        return;
    }
    fprintf(f, "Hello %s!\n", card->cid.name);
    fclose(f);
    ESP_LOGI(TAG, "File written");

    const char *file_foo = MOUNT_POINT "/foo.txt";

    // Check if destination file exists before renaming
    struct stat st;
    if (stat(file_foo, &st) == 0)
    {
        // Delete it if it exists
        unlink(file_foo);
    }

    // Rename original file
    ESP_LOGI(TAG, "Renaming file %s to %s", file_hello, file_foo);
    if (rename(file_hello, file_foo) != 0)
    {
        ESP_LOGE(TAG, "Rename failed");
        return;
    }

    // Open renamed file for reading
    ESP_LOGI(TAG, "Reading file %s", file_foo);
    f = fopen(file_foo, "r");
    if (f == NULL)
    {
        ESP_LOGE(TAG, "Failed to open file for reading");
        return;
    }

    // Read a line from file
    char line[64];
    fgets(line, sizeof(line), f);
    fclose(f);

    // Strip newline
    char *pos = strchr(line, '\n');
    if (pos)
    {
        *pos = '\0';
    }
    ESP_LOGI(TAG, "Read from file: '%s'", line);

    // All done, unmount partition and disable SDMMC peripheral
    esp_vfs_fat_sdcard_unmount(mount_point, card);
    ESP_LOGI(TAG, "Card unmounted");
}

/**
 * @brief wifiInit
 *
 */
static void app_wifi_init(void)
{
    ESP_LOGI(TAG, "[APP] Startup..");
    ESP_LOGI(TAG, "[APP] Free memory: %ld bytes", esp_get_free_heap_size());
    ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());

    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
     * Read "Establishing Wi-Fi or Ethernet Connection" section in
     * examples/protocols/README.md for more information about this function.
     */
    ESP_ERROR_CHECK(example_connect()); // 使用通过连接函数连接互联网
}

void app_main(void)
{
    // sdcard_test();//SD卡初始化
    app_wifi_init(); // 连接wifi初始化
    /*Little GL*/
    xTaskCreatePinnedToCore(gui_task, "gui_task", 1024 * 10, NULL, 5, NULL, 1);
    /*SNTP*/
    xTaskCreatePinnedToCore(Get_NTP_task, "getNTP task", 1024 * 4, NULL, 2, NULL, 0);
    /*Http Request*/
    xTaskCreatePinnedToCore(http_get_task, "http_get_task", 1024 * 10, NULL, 3, NULL, 0);
    /*temperature data*/
    // xTaskCreatePinnedToCore(temperature_sensor, "temperature_sensor", 1024 * 4, NULL, 6, NULL, 1);
    while (1)
    {
        vTaskDelay(1000 / portTICK_PERIOD_MS); // app_main()也被看作一个任务，所以需要设置任务切换
    }
    // vTaskDelete();//不会执行到此，但如果不加上面的死循环则必须用这个指令删除任务防止内存溢出或程序跑飞
}
